package socketVersion;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Queue;

public abstract class GenericServer{

	protected ServerSocket server;
	protected int port;
	protected static final int MAX_CONNECTIONS = 10;
	protected volatile Queue<Socket> connectionQueue;
	protected volatile Object lock = new Object();
	protected volatile boolean stopRunning = false;
	
	public GenericServer(int port) throws IOException{
		this.port = port;
		server = new ServerSocket(port);
		connectionQueue = new LinkedList<Socket>();
	}
	
	public void start(){
		
		Thread inboundHandler = new Thread(){
			public void run(){
				while(stopRunning == false){
					synchronized(lock){
						if(connectionQueue.isEmpty()){
					
						}
						else{
							Socket connection = connectionQueue.remove();
							handleConnection(connection);
						}
					}
				}
			}
		};
		inboundHandler.start();
		
		
		while(stopRunning == false){
			try {
				Socket inboundConnection = server.accept();
				synchronized(lock){
					
					if(connectionQueue.size() >= MAX_CONNECTIONS){
						dropConnection(inboundConnection);
					}
					else{
						connectionQueue.add(inboundConnection);
					}
				}
			} catch (IOException e) {
				//Dropped connection? Continue..
				continue;
			}
		}
	}

	private void handleConnection(Socket inboundConnection) {
		ObjectInputStream in;
		ObjectOutputStream out;
		Request request;
		try {
			in = new ObjectInputStream(inboundConnection.getInputStream());
			out = new ObjectOutputStream(inboundConnection.getOutputStream());
			
			request = (Request) in.readObject();
			String methodName = request.getMethodName();
			LinkedList<Object> args = request.getArgs();
			
			Reply r = handleMethod(methodName,args);
			
			out.writeObject(r);
			in.close();
			out.close();
			inboundConnection.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} 
	}


	private void dropConnection(Socket inboundConnection) {
		ObjectOutputStream out;
		
		try {
			out = new ObjectOutputStream(inboundConnection.getOutputStream());
			Reply r = new Reply(Reply.STATUS_SERVER_OVERLOAD,new LinkedList<Object>());
			out.writeObject(r);
			out.close();
			inboundConnection.close();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}

	protected abstract Reply handleMethod(String methodName,LinkedList<Object> args);

}
